﻿
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
namespace Anno.DynamicApi
{
    public static class PubFun
    {
        static PubFun()
        {
            cache = new Anno.EngineData.Cache.MemoryCache("IMemoryCache");
        }
        #region BaseDirectory
        public static string BaseDirectory
        {
            get
            {
                string location = AppDomain.CurrentDomain.BaseDirectory.Substring(0, AppDomain.CurrentDomain.BaseDirectory.LastIndexOf('\\'));
                return location;
            }
        }
        #endregion
        #region 缓存数据处理
        private static Anno.EngineData.Cache.ICache cache = null;
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <returns></returns>
        public static T CacheGet<T>(string key)
        {
            return cache.Get<T>(key);
        }
        /// <summary>
        /// 从缓存中获取数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="cachePopulate">>获取缓存值的操作(当缓存不存在相应的数据时，执行此方法获取数据)</param>
        /// <param name="expiresTime">缓存时长(分钟), enum ExpiresTime</param>
        /// <param name="isSliding">是否滑动过期（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
        /// <returns></returns>
        public static T CacheGet<T>(string key, Func<T> cachePopulate, EngineData.Cache.ExpiresTime expiresTime = EngineData.Cache.ExpiresTime.Minutes_30, Func<T, bool> checkPopulate = null)
        {
            return cache.Get<T>(key, cachePopulate, expiresTime, checkPopulate);
        }
        /// <summary>
        /// 设置缓存数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="value">该缓存项的数据</param>
        /// <param name="expiresTime">缓存时长(分钟), enum ExpiresTime</param>
        /// <param name="isSliding">是否滑动过期（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
        /// <returns></returns>
        public static bool CacheSet(string key, object value, EngineData.Cache.ExpiresTime expiresTime = EngineData.Cache.ExpiresTime.Minutes_30)
        {
            return cache.Set(key, value, TimeSpan.FromMinutes( expiresTime.GetHashCode()));
        }
        /// <summary>
        /// 新增或更新缓存
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <param name="value">缓存项的数据</param>
        /// <param name="expirationMinutes">缓存时长(分钟)</param>
        /// <param name="isSliding">是否滑动过期（如果在过期时间内有操作，则以当前时间点延长过期时间）</param>
        /// <returns></returns>
        public static bool CacheSet(string key, object value, int expirationMinutes = 30)
        {
            return cache.Set(key, value, expirationMinutes);
        }
        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key">缓存项的唯一标识符</param>
        /// <returns></returns>
        public static bool CacheRemove(string key)
        {
            return cache.Del(key)>0;
        }
        #endregion

        #region 读/写文件
        /// <summary>
        /// 读取文件
        /// </summary>
        /// <param name="path">E:\\test.txt</param>
        /// <returns></returns>
        public static string Read(string path)
        {
            var rtStr = "";
            try
            {
                var pat = System.IO.Path.GetDirectoryName(path);
                if (pat.IsNullOrEmpty())
                {
                    pat = BaseDirectory;
                    path = System.IO.Path.Combine(pat, path);
                }
                if (!System.IO.Directory.Exists(pat)) System.IO.Directory.CreateDirectory(pat);
                StreamReader sr = new StreamReader(path, Encoding.Default);
                System.String line;
                while ((line = sr.ReadLine()) != null)
                {
                    rtStr += line.ToString();
                }
            }
            catch (IOException e)
            {
                rtStr = e.ToString();
            }
            return rtStr;
        }

        /// <summary>
        /// 写入文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="content"></param>
        public static void Write(string path, string content)
        {
            var pat = System.IO.Path.GetDirectoryName(path);
            if (pat.IsNullOrEmpty())
            {
                pat = BaseDirectory;
                path = System.IO.Path.Combine(pat, path);
            }
            if (!System.IO.Directory.Exists(pat)) System.IO.Directory.CreateDirectory(pat);
            FileStream fs = new FileStream(path, FileMode.Create);
            StreamWriter sw = new StreamWriter(fs);
            //开始写入
            sw.Write(content);
            //清空缓冲区
            sw.Flush();
            //关闭流
            sw.Close();
            fs.Close();
        }
        #endregion

        #region GetAppSettings
        /// <summary>
        /// 获取AppSetting配置
        /// </summary>
        /// <param name="strKey"></param>
        /// <param name="def"></param>
        /// <returns></returns>
        public static string GetAppSettings(string strKey, string def = "")
        {
            return Const.AppSettings.GetAppSettings(strKey, def);
        }
        #endregion

        #region SetSqlEntityParameter
        /// <summary>
        /// 实体或JSON对象转SQL参数
        /// </summary>
        /// <param name="sqlEntity"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static bool SetSqlEntityParameter(Repository.SqlEntity sqlEntity, object obj, string paramSuffix = "", List<ApiFieldInfo> ApiInputInfoList = null)
        {
            if (sqlEntity == null || obj == null || obj == DBNull.Value)
                return false;
            var _dicObject = obj.ToDictionary();
            foreach (var item in _dicObject)
            {
                var val = item.Value;

                var inputf = ApiInputInfoList?.Find(f => f.Name.Equals(item.Key, StringComparison.OrdinalIgnoreCase));
                if (inputf != null && val != null)
                {
                    var typeoff = GetTypeByName(inputf.SqlTypeName);
                    if (typeoff != null)
                        sqlEntity.AddParameter(paramSuffix + item.Key, val.ChangeType(typeoff));
                    continue;
                }

                sqlEntity.AddParameter(paramSuffix + item.Key.Replace("-","_").Replace(".", "_"), val != null ? val : null);
            }
            return true;
        }

        public static object GetActionFieldValue(object input, ApiFieldInfo actionFieldInfo)
        {
            if (actionFieldInfo == null || input == null || input == DBNull.Value)
                return null;

            var val = Exts.GetFieldPathValue(input, actionFieldInfo.ValuePath);
            if (val != null)
            {
                if (actionFieldInfo.SubFieldInfoList?.Count > 0 && val is Newtonsoft.Json.Linq.JObject _sjObject)
                {
                    return ToDataTable(_sjObject, null, actionFieldInfo.SubFieldInfoList);
                }
                else if (actionFieldInfo.SubFieldInfoList?.Count > 0 && val is Newtonsoft.Json.Linq.JArray _ajObject)
                {
                    return ToDataTable(_ajObject, actionFieldInfo.SubFieldInfoList);
                }
                //if (val is Newtonsoft.Json.Linq.JObject)
                //{
                //    return GetActionFieldValue(val, actionFieldInfo);
                //}
                //else if (val is Newtonsoft.Json.Linq.JArray)
                //{
                //}
                else
                {
                    if (val != null)
                    {
                        var typeoff = GetTypeByName(actionFieldInfo.SqlTypeName);
                        if (typeoff != null)
                        {
                            if (val is Newtonsoft.Json.Linq.JObject || val is Newtonsoft.Json.Linq.JArray)
                            {
                                return val.SerializeObjectToJson();
                            }
                            else if (val is Newtonsoft.Json.Linq.JValue)
                            {
                                return (val as Newtonsoft.Json.Linq.JValue).Value.ChangeType(typeoff);
                            }
                            return val;
                        }
                    }
                }
            }
            else if (actionFieldInfo.TypeName.Trim().ToUpper() == "JSON")
            {
                if (input != null)
                {
                    var typeoff = GetTypeByName(actionFieldInfo.SqlTypeName);
                    if (typeoff != null)
                    {
                        if (input is Newtonsoft.Json.Linq.JObject || input is Newtonsoft.Json.Linq.JArray)
                        {
                            return input.SerializeObjectToJson();
                        }
                        else if (input is Newtonsoft.Json.Linq.JValue)
                        {
                            return (input as Newtonsoft.Json.Linq.JValue).Value.ChangeType(typeoff);
                        }
                    }
                }
                return input;
            }
            return null;
        }
        /// <summary>
        /// Newtonsoft.Json.Linq.JObject对象转DataTable
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static DataTable ToDataTable(Newtonsoft.Json.Linq.JObject obj, DataTable dt, List<ApiFieldInfo> actionFieldInfos)
        {
            if (obj == null || actionFieldInfos == null || actionFieldInfos.Count == 0)
                return null;
            if (dt == null)
            {
                dt = new DataTable();
                foreach (var actionField in actionFieldInfos)
                {
                    var typeoff = GetTypeByName(actionField.SqlTypeName);
                    if (typeoff == null) typeoff = GetTypeByName("string");

                    dt.Columns.Add(actionField.SqlName, typeoff);
                }
            }
            //---------------------------------------------------------------------------------
            var dr = dt.NewRow();
            ToDataRow(obj, dr, actionFieldInfos);
            dt.Rows.Add(dr);

            return dt;
        }
        /// <summary>
        /// Newtonsoft.Json.Linq.JObject对象转DataRow
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        private static DataRow ToDataRow(Newtonsoft.Json.Linq.JObject obj, DataRow dr, List<ApiFieldInfo> actionFieldInfos)
        {
            if (dr == null || obj == null || actionFieldInfos == null || actionFieldInfos.Count == 0)
                return null;
            //---------------------------------------------------------------------------------
            foreach (DataColumn col in dr.Table.Columns)
            {
                var val = GetActionFieldValue(obj, actionFieldInfos.Find(f => f.SqlName == col.ColumnName));
                dr[col.ColumnName] = val;
            }

            return dr;
        }
        /// <summary>
        /// Newtonsoft.Json.Linq.JArray对象转DataTable
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static DataTable ToDataTable(Newtonsoft.Json.Linq.JArray obj, List<ApiFieldInfo> actionFieldInfos)
        {
            if (obj == null || actionFieldInfos == null || actionFieldInfos.Count == 0)
                return null;
            DataTable dt = new DataTable();
            foreach (var actionField in actionFieldInfos)
            {
                var typeoff = GetTypeByName(actionField.SqlTypeName);
                if (typeoff == null) typeoff = GetTypeByName("string");

                dt.Columns.Add(actionField.SqlName, typeoff);
            }
            //---------------------------------------------------------------------------------
            foreach (var item in obj)
            {
                if (item.HasValues)
                {
                    var val = item.Value<object>();
                    if (val != null)
                    {
                        if (val is Newtonsoft.Json.Linq.JObject _sjObject)
                        {
                            var dr = dt.NewRow();
                            ToDataRow(_sjObject, dr, actionFieldInfos);
                            dt.Rows.Add(dr);
                        }
                        continue;
                    }
                }
            }
            return dt;
        }
        //============================================================================================================================
        /// <summary>
        /// 取对象属性值（如：id 、userInfo.id）
        /// </summary>
        /// <param name="input"></param>
        /// <param name="filedPath"></param>
        /// <param name="typeName"></param>
        /// <returns></returns>
        public static object GetFieldPathValue(this object input, string filedPath, string typeName, object defVal = null)
        {
            if (filedPath.IsNullOrEmpty_() || input.IsNullOrEmpty_())
                return defVal;
            var val = Exts.GetFieldPathValue(input, filedPath, typeName);
            if (val != null)
            {
                return val;
            }
            return defVal;
        }



        /// <summary>
        /// 取中括号[]值
        /// </summary>
        /// <param name="template"></param>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static string GetMatchValue(string template, string left = "[", string right = "]")
        {
            var rgx = new System.Text.RegularExpressions.Regex(@"(?i)(?<=\" + left + @")(.*)(?=\" + right + ")");//中括号[]
            var rt = rgx.Match(template).Value;//中括号[]
            return rt;
        }
        #region GetTypeByDataTypeName

        public static Type GetTypeByName(string typeName)
        {
            var type = GetTypeByString(typeName);
            if (type != null)
            {
                return type;
            }
            //------------------------------------------------------------------------
            Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();
            int assemblyArrayLength = assemblyArray.Length;
            for (int i = 0; i < assemblyArrayLength; ++i)
            {
                type = assemblyArray[i].GetType(typeName);
                if (type != null)
                {
                    return type;
                }
            }
            //------------------------------------------------------------------------
            //for (int i = 0; (i < assemblyArrayLength); ++i)
            //{
            //    Type[] typeArray = assemblyArray[i].GetTypes();
            //    int typeArrayLength = typeArray.Length;
            //    for (int j = 0; j < typeArrayLength; j++)
            //    {
            //        try
            //        {
            //            var tye = typeArray[j];
            //            if (tye != null && tye.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase))
            //            {
            //                return tye;
            //            }
            //        }
            //        catch { }
            //    }
            //}
            return type;
        }
        public static Type GetTypeByString(string type)
        {
            switch (type.ToLower())
            {
                case "bool":
                    return Type.GetType("System.Boolean", false, true);
                case "byte":
                    return Type.GetType("System.Byte", false, true);
                case "sbyte":
                    return Type.GetType("System.SByte", false, true);
                case "char":
                    return Type.GetType("System.Char", false, true);
                case "decimal":
                    return Type.GetType("System.Decimal", false, true);
                case "double":
                    return Type.GetType("System.Double", false, true);
                case "single":
                case "float":
                    return Type.GetType("System.Single", false, true);
                case "int32":
                case "int":
                    return Type.GetType("System.Int32", false, true);
                case "uint32":
                case "uint":
                    return Type.GetType("System.UInt32", false, true);
                case "int64":
                case "long":
                    return Type.GetType("System.Int64", false, true);
                case "uint64":
                case "ulong":
                    return Type.GetType("System.UInt64", false, true);
                case "object":
                    return Type.GetType("System.Object", false, true);
                case "int16":
                case "short":
                    return Type.GetType("System.Int16", false, true);
                case "uint16":
                case "ushort":
                    return Type.GetType("System.UInt16", false, true);
                case "string":
                    return Type.GetType("System.String", false, true);
                case "date":
                case "datetime":
                    return Type.GetType("System.DateTime", false, true);
                case "guid":
                    return Type.GetType("System.Guid", false, true);
                default:
                    return Type.GetType(type, false, true);
            }
        }
        #endregion
        #endregion

        #region GetXmlNodeAttributeValue
        /// <summary>
        /// 选择xmlNode节点的匹配xmlAttributeName的属性XmlAttribute.
        /// </summary>
        /// <param name="xmlNode">X节点</param>
        /// <param name="xmlAttributeName">要匹配xmlAttributeName的属性名称</param>
        /// <returns>返回xmlAttributeName</returns>
        public static string GetXmlNodeAttributeValue(XmlNode xmlNode, string xmlAttributeName, string defValue = "")
        {
            try
            {
                if (xmlNode != null && xmlNode.Attributes.Count > 0)
                {
                    var val = xmlNode.Attributes[xmlAttributeName];
                    if (!val.IsNullOrEmpty() && !val.Value.IsNullOrEmpty())
                        return val.Value;
                }
            }
            catch
            {
            }
            return defValue;
        }
        #endregion

        #region CSScriptCodeInvoke
        /// <summary>
        /// 动态编译引擎-CS-Script
        /// </summary>
        /// <param name="scriptCode"></param>
        /// <param name="fullClassName"></param>
        /// <param name="actionMethod"></param>
        /// <returns></returns>
        public static object CSScriptCodeInvoke(string scriptCode, string fullClassName, string actionMethod, params object[] paramters)
        {
            var assembly = RuntimeCodeCompiler.CompileCodeOrGetFromCache(scriptCode, actionMethod);
            if (assembly == null)
            {
                throw new Exception(fullClassName + " asmb == null @CSScriptCodeInvoke");
            }
            Type svcType = assembly.GetType($"css_root+{fullClassName}"); //assembly.GetType($"{fullClassName}");
            if (svcType == null)
            {
                throw new Exception(fullClassName + " svcType == null @CSScriptCodeInvoke");
            }
            var methodInfo = svcType.GetMethod(actionMethod);
            //var parameters = methodInfo.GetParameters();
            //object[] Args = null;
            //if (input != null && parameters.Length > 0)
            //{
            //    Args = PubFun.CreateArgs(parameters, input);
            //}
            return methodInfo.FastInvoke(svcType, paramters);
        }

        /// <summary>
        /// 动态编译引擎-CS-Script
        /// </summary>
        /// <param name="script"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public static object CSScriptCodeInvoke(string script, object input)
        {
            if (input == null || input == DBNull.Value || script == null || script == "")
                return "";
            return Exts.GetFieldPathValue(input, script);
            var fullClassName = "CSClass_ActionMethodMain";// + Guid.NewGuid().ToString().Replace("-","")+ "CS";
            var actionMethod = "ActionMethodMain";

            var scriptCode = new StringBuilder();
            scriptCode.AppendLine("using System;");
            scriptCode.AppendLine("using System.Collections.Generic;");
            //scriptCode.AppendLine("using Anno.DynamicApi.Entity;");
            scriptCode.AppendLine("");
            //scriptCode.AppendLine("");
            //scriptCode.AppendLine("using Anno.DynamicApi.Common;");
            scriptCode.AppendLine("using System.Dynamic;");

            scriptCode.AppendLine($"public class {fullClassName}");
            scriptCode.AppendLine("{");
            scriptCode.AppendLine($"   public object {actionMethod}(dynamic input)");
            scriptCode.AppendLine("   {");
            scriptCode.AppendLine($"return {script};");
            scriptCode.AppendLine("    }");
            scriptCode.AppendLine("}");

            var ad = AppDomain.CreateDomain(fullClassName);
            RuntimeCodeCompiler.OtherAssemblys.Remove("Microsoft.CSharp.dll");
            RuntimeCodeCompiler.OtherAssemblys.Add("Microsoft.CSharp.dll");
            var assembly = RuntimeCodeCompiler.CompileCode(scriptCode.ToString(), "");
            if (assembly == null)
            {
                throw new Exception(fullClassName + " asmb == null @CSScriptCodeInvoke");
            }
            //ad.Load(assembly.GetName());

            var obj = assembly.CreateInstance($"{fullClassName}");
            if (obj == null)
            {
                throw new Exception(fullClassName + " obj == null @CSScriptCodeInvoke");
            }
            var methodInfo = obj.GetType().GetMethod(actionMethod);

            var dyinput = input.ToDynamic();
            object[] Args = new object[] { dyinput };

            var ret = methodInfo.Invoke(obj, Args);
            AppDomain.Unload(ad);
            return ret;
        }
        #endregion

        #region SetPropertyValue
        public static bool SetPropertyValue(object obj, string propertyName, object value)
        {
            if (obj == null || obj == DBNull.Value || propertyName.IsNullOrEmpty())
                return false;
            var pre = obj.GetType().GetProperty(propertyName,
                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
            if (pre != null && pre.CanWrite)
            {
                //pre.SetValue(obj, value, BindingFlags.GetField | BindingFlags.GetProperty, null, null, CultureInfo.CurrentCulture);
                // value = value.ChangeType(pre.PropertyType);
                pre.SetValue(obj, value, null);
                return true;
            }
            else
            {
                var filed = obj.GetType().GetField(propertyName,
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
                if (filed != null)
                {
                    filed.SetValue(obj, value);
                    return true;
                }
            }

            return false;
        }
        public static bool PropertyExsts(object obj, string propertyName)
        {
            if (obj == null || obj == DBNull.Value || propertyName.IsNullOrEmpty())
                return false;
            var pre = obj.GetType().GetProperty(propertyName,
                BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
            if (pre != null && pre.CanWrite)
            {
                return true;
            }
            else
            {
                var filed = obj.GetType().GetField(propertyName,
                    BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static);
                if (filed != null)
                {
                    return true;
                }
            }

            return false;
        }
        #endregion

        #region GetHeaderValue
        ///// <summary>
        ///// 获取当前Header值
        ///// </summary>
        ///// <param name="key"></param>
        ///// <returns></returns>
        //public static string GetHeaderValue(string key, string defult = "")
        //{
        //    IDictionary<string, string> headers = GetDicHeaders();
        //    return GetHeaderValue(headers, key, defult);
        //}
        //public static string GetHeaderValue(Rpc.Client.Request request, string key, string defult = "")
        //{
        //    IDictionary<string, string> headers = GetDicHeaders(request);
        //    return GetHeaderValue(headers, key, defult);
        //}
        //public static string GetHeaderValue(IDictionary<string, string> headers, string key, string defult = "")
        //{
        //    var val = "";
        //    if (headers?.TryGetValue(key, out val) == true && val.IsNotNullOrEmpty())
        //        return val;
        //    else return defult;
        //}
        ///// <summary>
        ///// 获取当前Headers的dynamic对象
        ///// </summary>
        ///// <param name="key"></param>
        ///// <returns></returns>
        //public static dynamic GetHeaders()
        //{
        //    return GetDicHeaders().ToDynamic();
        //}
        //public static IDictionary<string, string> GetDicHeaders()
        //{
        //    return GetDicHeaders(App.HttpContext.Request);
        //}

        //public static IDictionary<string, string> GetDicHeaders(this Rpc.Client.Request request)
        //{
        //    return GetDicHeaders(request?.);
        //}
        //public static IDictionary<string, string> GetDicHeaders(this HttpRequest request)
        //{
        //    IDictionary<string, string> headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        //    var hd = request?.Headers;
        //    if (hd?.Count > 0)
        //    {
        //        foreach (var hdk in hd?.Keys)
        //        {
        //            headers[hdk] = hd[hdk];
        //        }
        //    }
        //    return headers;
        //}

        //public static IDictionary<string, string> GetQueryParameters()
        //{
        //    return GetQueryParameters(App.HttpContext.Request);
        //}
        //public static IDictionary<string, string> GetQueryParameters(this Request request)
        //{
        //    return GetQueryParameters(request?.HttpRequest);
        //}

        //public static IDictionary<string, string> GetQueryParameters(this HttpRequest request)
        //{
        //    IDictionary<string, string> parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        //    var par = request?.Query;
        //    if (par?.Count > 0)
        //    {
        //        foreach (var park in par?.Keys)
        //        {
        //            parameters[park] = par[park];
        //        }
        //    }
        //    return parameters;
        //}

        //public static string GetBodyContent(this HttpRequest HttpRequest)
        //{
        //    var _bodyContent = "";

        //    using (var readStream = new StreamReader(HttpRequest.Body, Encoding.UTF8))
        //    {
        //        _bodyContent = readStream.ReadToEnd();
        //    }
        //    return _bodyContent;
        //}
        #endregion

        #region GetServerByHash
        /// <summary>
        /// 负载均衡算法--源哈希法（Hash）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="serverList"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T GetServerByHash<T>(List<T> serverList, object source)
        {
            if (serverList.IsNullOrEmpty_())
            {
                return default(T);
            }
            if (serverList.Count == 1)
            {
                return serverList[0];
            }
            if (source == null) source = serverList;
            var hs = Math.Abs(source.GetHashCode());
            var index = hs % serverList.Count;
            return serverList[index];
        }
        #endregion
    }
}